/**
 * Copyright (c) 2009 Teoti Graphix, LLC See:
 *     http://tkarchitecture.teotigraphix.org
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 * 
 * @author Michael Schmalle mschmalle@teotigraphix.com
 */
package org.teotigraphix.dx.core
{

import flash.events.IEventDispatcher;
import flash.system.ApplicationDomain;
import flash.utils.getQualifiedClassName;

import mx.core.IInvalidating;
import mx.core.Singleton;
import mx.events.PropertyChangeEvent;
import mx.utils.DescribeTypeCache;
import mx.utils.DescribeTypeCacheRecord;

import org.teotigraphix.dx.patterns.manager.MetaManager;
import org.teotigraphix.dx.patterns.manager.meta.ClassMetaData;
import org.teotigraphix.dx.patterns.manager.meta.EventHandlerInfo;

/**
 * The dx framework utility for commonly used methods
 * and other various functionality. 
 * 
 * @copyright Teoti Graphix, LLC
 * @author Michael Schmalle
 * @date 12-04-08, 07-07-09
 */
public class ToolKitUtil
{
	include "Version.as"
	
	//--------------------------------------------------------------------------
	//
	//  Public Class EventHandlerInfo :: Methods
	//
	//--------------------------------------------------------------------------
	
	// TODO (mschmalle) make EventHandlerPhase enum
	
	/**
	 * @private
	 */
	public static function removeEventHandler( 
		component:IEventDispatcher,
		handler:Function, directive:EventHandlerInfo):void
	{
		if(!component)
			return;
				
		switch (directive.phase)
		{
			case "capture":
				component.removeEventListener(
					directive.event, handler, true);
				break;
				
			case "both":
				component.removeEventListener(
					directive.event, handler, true);
				component.removeEventListener(
					directive.event, handler, false);
				break;
			
			default:
				component.removeEventListener(
					directive.event, handler, false);
				break;
		}
	}
	
	/**
	 * @private
	 */
	public static function addEventHandler( 
		component:IEventDispatcher,
		handler:Function, directive:EventHandlerInfo):void
	{
		if(!component)
			return;
		
		switch (directive.phase)
		{
			case "capture":
				component.addEventListener(
					directive.event, handler, true, 
					directive.priority, directive.weak);
				break;
				
			case "both":
				component.addEventListener(
					directive.event, handler, true, 
					directive.priority, directive.weak);
				component.addEventListener(
					directive.event, handler, false, 
					directive.priority, directive.weak);
				break;
			
			default:
				component.addEventListener(
					directive.event, handler, false, 
					directive.priority, directive.weak);
				break;
		}
	}
	
	/**
	 * Dispatches a <code>PropertyChangeEvent</code> from the <code>source</code>.
	 * 
	 * @param source The source that is dispatching the property change event.
	 * @param property The changing property name.
	 * @param oldValue The sources old property value.
	 * @param newValue The sources new property value.
	 */
	public static function dispatchChangeEvent(
		source:Object, property:String, 
		oldValue:Object, newValue:Object):void
	{
		var e:PropertyChangeEvent = new PropertyChangeEvent(
			property + "Changed", false, false, "update", property,
			oldValue, newValue, source);
		
		if (source is IInvalidating)
			IInvalidating(source).invalidateProperties();
		
		if (source is IEventDispatcher)
			IEventDispatcher(source).dispatchEvent(e);
	}
	
	//--------------------------------------------------------------------------
	//
	//  Public Class Utility :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * Resolves a class's runtime metadata tags, returning a 
	 * <code>ClassMetaData</code> instance.
	 * 
	 * @param instance The instance to resolve <code>ClassMetaData</code> for.
	 * 
	 * @uses org.teotigraphix.dx.manager.MetaManager#resolveClassMetaData()
	 */
	public static function resolveClassMetaData(instance:Object):ClassMetaData
	{
		return MetaManager.resolveClassMetaData(instance);
	}
		
	/**
	 * Returns a singleton that has been registered to the toolkit.
	 * 
	 * <p>Use <code>ToolKitUtil.registerSingleton()</code> to register the 
	 * singleton instance.</p>
	 * 
	 * @param api The api of the singleton to return.
	 */
	public static function getSingleton(api:String):Object
	{
		return Singleton.getInstance(api);
	}
	
	/**
	 * Registers a singleton class with an api.
	 * 
	 * <p>Use <code>ToolKitUtil.getSingleton()</code> to return the 
	 * singleton instance.</p>
	 * 
	 * @param api The api of the singleton to register.
	 * @param clazz The Class of the singleton to register.
	 */
	public static function registerSingleton(api:String, clazz:Class):void
	{
		Singleton.registerClass(api, clazz);
	}
	
	/**
	 * Returns the qualified class name of the instance.
	 * 
	 * <p>Wraps <code>flash.utils.getQualifiedClassName()</code>.</p>
	 * 
	 * @param instance The instance to return the class name for.
	 * @param qualified A boolean indicating whether to return the qualified
	 * name with :: or just a className.
	 * @param replace Whether to replace the double colan with a period.
	 */
	public static function getClassName(
		instance:Object, qualified:Boolean = true, replace:Boolean = true):String
	{
		var name:String = getQualifiedClassName(instance);
		if (!qualified)
			return name.split("::").pop() as String;
		else if (replace)
			name = name.split("::").join(".");
			
		return name;
	}
	
	/**
	 * Returns a class definition for the currentDomain.
	 * 
	 * @param type A string indicating the class type to return.
	 */
	public static function getDefinition(type:String):Object
	{
		return ApplicationDomain.currentDomain.getDefinition(type);
	}
	
	/**
	 * Returns a Class based on a current instance.
	 * 
	 * @param instance An instance to retreive it's Class.
	 */
	public static function getClass(instance:Object):Class
	{
		return getDefinition(getClassName(instance)) as Class;
	}
	
	/**
	 * Returns XML reflection of the api Class|Object.
	 * 
	 * @param api The class or instance to be reflected.
	 */
	public static function getReflection(api:Object):XML
	{
		var record:DescribeTypeCacheRecord = 
			DescribeTypeCache.describeType(api);
		return record.typeDescription;	
	}
}
}